﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FlashCards.NeuralNetworks;
using FlashCards.Tests.NeuralNetworkTest.Simulations;
using FlashCards.Tests.NeuralNetworkTest.Extensions;

namespace FlashCards.Tests.NeuralNetworkTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //var network = new NeuralNetwork(2,5,3);

            //var simulator = new LearningSetsPreparator(5, 3);

            //int lastIteration = network.TrainWholeSet(simulator.InputSets, simulator.PatternSet, 10, 0.05);
            //Console.WriteLine("Learning completed in iteration: " + lastIteration);

            //for (int i = 0; i < simulator.InputSets.Count; i++)
            //{
            //    ResultPresenter.WriteResults(network.GetResult(simulator.InputSets[i]));
            //}

            //simulator.SimulateUserProgress(3);
            //simulator.SimulateUserProgress(2);
            //simulator.SimulateUserProgress(2);

            //lastIteration = network.TrainWholeSet(simulator.InputSets, simulator.PatternSet, 3, 0.1);

            //simulator.SimulateUserProgress(0);

            //lastIteration = network.TrainWholeSet(simulator.InputSets, simulator.PatternSet, 3, 0.1);

            //simulator.SimulateUserProgress(3);
            //simulator.SimulateUserProgress(4);

            //lastIteration = network.TrainWholeSet(simulator.InputSets, simulator.PatternSet, 3, 0.1);

            //simulator.WritePatternSets();

            //Console.WriteLine("Learning completed in iteration: " + lastIteration);
            //for (int i = 0; i < simulator.InputSets.Count; i++)
            //{
            //    ResultPresenter.WriteResults(network.GetResult(simulator.InputSets[i]));
            //}



            //int id = TestNetworkFromFlashcardSet();
            //TestNetworkLoading(id);

            //TestNetworkModifying(8);

            //TestNetworkLoading(12, 64);

            TestLearningProgress(9, 40);
            TestLearningProgress(9, 41);
            TestLearningProgress(9, 42);
            TestLearningProgress(9, 43);
        }

        public static void TestLearningProgress(int userId, int flashcardSetId)
        {
            var rand = new Random();

            for (int i = 0; i < 7; i++)
            {
                using (var context = new Model.FlashCardsEntities())
                {

                    var set = context.FlashcardSets.FirstOrDefault(x => x.FlashcardSetId == flashcardSetId);
                    var repeat = context.Repeats.Where(x => x.UserId == userId)
                        .Where(x => x.FlashcardSetId == flashcardSetId)
                        .OrderByDescending(x => x.RepeatDate)
                        .FirstOrDefault();

                    var increaseRatio = (set.Flashcards.Count * 0.10);
                    var decreaseRatio = (set.Flashcards.Count * 0.02);

                    var increase = rand.Next(2) * increaseRatio;
                    var descrese = rand.Next(2) * decreaseRatio;

                    var marks = RepeatsSimulator.PrepareMarksForSet(set, repeat, increase, descrese);
                    RepeatsSimulator.AddNewRepeat(marks, set);
                }
            }
        }

        public static void TestMarkCalculations(int userId, int flashcardSetId)
        {
            using (var context = new FlashCards.Model.FlashCardsEntities())
            {
                var data = new List<FlashCards.Model.NeuralNetworkResults>();

                AddResult(0, 0, 1, 0.2m, data);
                AddResult(0, 0, 1, 0, data);
                AddResult(0, 0.2m, 1, 0, data);
                AddResult(0.2m, 0, 1, 0, data);

                AddResult(0, 0, 1, 0.4m, data);
                AddResult(0, 0, 1, 0, data);
                AddResult(0, 0.4m, 1, 0, data);
                AddResult(0.4m, 0, 1, 0, data);

                AddResult(0, 0, 1, 0.6m, data);
                AddResult(0, 0, 1, 0, data);
                AddResult(0, 0.6m, 1, 0, data);
                AddResult(0.6m, 0, 1, 0, data);

                AddResult(0, 0, 1, 0.8m, data);
                AddResult(0, 0, 1, 0, data);
                AddResult(0, 0.8m, 1, 0, data);
                AddResult(0.8m, 0, 1, 0, data);

                foreach (var items in data)
                {
                    Console.WriteLine(string.Format("U:{0}\tP:{1}\tA:{2}\tG:{3}\t=M:{4}", items.Unknown, items.Poor, items.Average, items.Good, NeuralNetworkUtils.CalculateFlashcardsMark(items)));
                }

            }
        }

        private static void AddResult(decimal u, decimal p, decimal a, decimal g, List<FlashCards.Model.NeuralNetworkResults> list)
        {
            list.Add(new Model.NeuralNetworkResults
            {
                Unknown = u,
                Poor = p,
                Average = a,
                Good = g
            });
        }

        public static int TestNetworkFromFlashcardSet()
        {
            using (var context = new FlashCards.Model.FlashCardsEntities())
            {
                var repeat = context.Repeats
                    .Include("FlashcardSet")
                    .Include("FlashcardSet.Flashcards")
                    .FirstOrDefault(x => x.RepeatId == 42);

                var simulator = new LearningSetsPreparator(repeat);
                var flashcardIds = repeat.FlashcardSet.Flashcards.Select(x => x.FlashcardId).ToList();

                var network = new NeuralNetwork(2, 35, 4, repeat.FlashcardSetId, flashcardIds);

                network.TrainWholeSet(simulator.InputSets, simulator.PatternSet, 10, 0.1);

                for (int i = 0; i < simulator.InputSets.Count; i++)
                {
                    ResultPresenter.WriteResults(network.GetResult(simulator.InputSets[i]));
                }

                var entityNetwork = network.ToEntity();
                context.NeuralNetworks1.AddObject(entityNetwork);
                context.SaveChanges();

                return entityNetwork.NetworkId;
            }
        }

        public static void TestNetworkLoading(int id, int repeatId)
        {
            using (var context = new FlashCards.Model.FlashCardsEntities())
            {
                var network = context.NeuralNetworks1
                    .Include("NeuronsLayers")
                    .Include("NeuronsLayers.ai_Neurons")
                    .Include("NeuronsLayers.ai_Neurons.NeuronWeights")
                    .Include("FlashcardSet")
                    .Include("FlashcardSet.Flashcards")
                    .FirstOrDefault(x => x.NetworkId == id);

                var testNetwork = network.CreateNetwork();

                var repeat = context.Repeats
                    .Include("FlashcardSet")
                    .Include("FlashcardSet.Flashcards")
                    .FirstOrDefault(x => x.RepeatId == repeatId);

                var simulator = new LearningSetsPreparator(repeat);

                foreach (var pair in simulator.InputDictionarySets)
                {
                    ResultPresenter.WriteResults(testNetwork.GetResult(pair));
                }

                foreach (var list in simulator.PatternSet)
                {
                    foreach (var number in list)
                    {
                        Console.Write(number);
                        Console.Write('\t');
                    }
                    Console.Write('\n');
                }

                testNetwork.TrainWholeSet(simulator.InputDictionarySets, simulator.PatternSet, 2, 0.1);

                Console.WriteLine();
                Console.WriteLine();

                foreach (var pair in simulator.InputDictionarySets)
                {
                    ResultPresenter.WriteResults(testNetwork.GetResult(pair));
                }

                Console.WriteLine();
            }
        }

        public static void TestNetworkModifying(int id)
        {
            using (var context = new FlashCards.Model.FlashCardsEntities())
            {
                var network = context.NeuralNetworks1
                        .Include("NeuronsLayers")
                        .Include("NeuronsLayers.ai_Neurons")
                        .Include("NeuronsLayers.ai_Neurons.NeuronWeights")
                        .Include("FlashcardSet")
                        .Include("FlashcardSet.Flashcards")
                        .FirstOrDefault(x => x.NetworkId == id);

                var testNetwork = network.CreateNetwork();

                var repeat = context.Repeats
                    .Include("FlashcardSet")
                    .Include("FlashcardSet.Flashcards")
                    .FirstOrDefault(x => x.RepeatId == 40);

                foreach (var item in repeat.FlashcardSet.Flashcards)
                {
                    if (repeat.RepeatItems.FirstOrDefault(x => x.FlashcardId == item.FlashcardId) == null)
                    {
                        repeat.RepeatItems.Add(new Model.RepeatItem
                        {
                            FlashcardId = item.FlashcardId,
                            RepeatMark = 0,
                        });
                    }

                    if (network.NeuronsLayers.ToList()[0].ai_Neurons.FirstOrDefault(x => x.FlashcardId == item.FlashcardId) == null)
                    {
                        testNetwork.AddInput(item.FlashcardId);
                    }
                }

                var simulator = new LearningSetsPreparator(repeat);

                for (int i = 0; i < simulator.InputSets.Count; i++)
                {
                    ResultPresenter.WriteResults(testNetwork.GetResult(simulator.InputSets[i]));
                }

                simulator.SimulateUserProgress(0);
                simulator.SimulateUserProgress(2);

                testNetwork.TrainWholeSet(simulator.InputSets, simulator.PatternSet, 3, 0.1);

                Console.WriteLine();
                Console.WriteLine();

                for (int i = 0; i < simulator.InputSets.Count; i++)
                {
                    ResultPresenter.WriteResults(testNetwork.GetResult(simulator.InputSets[i]));
                }

                var updatedNetwork = testNetwork.ToEntity();
                network = updatedNetwork.UpdateNetwork(network);

                context.SaveChanges();
            }
        }
    }
}
